#include "alog.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h> 
#include <sys/stat.h>  
#include <sys/time.h>


#define CLOG_BUF_LENGTH	    (1024)
#define CLOG_PATH_LENGTH	(300)
#define CLOG_MESSAGE_FORMAT	 "[%s]%s : %s"

#ifdef _ANDROID
#define LOG_TAG     "hls*****"
#include <android/log.h>  // NDK env,    LOCAL_LDLIBS := -llog
//#include <utils/Log.h>    // android env,LOCAL_SHARED_LIBRARIES := libutils

inline static void android_OutDebugString(int level, const char *format, ... )
{
	va_list ap;
	va_start(ap, format);
	char buf[2048]={0};
	vsnprintf(buf, 2048, format, ap);
	va_end(ap);

        __android_log_write(level, LOG_TAG, buf);
}
//#define jo_printf(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define jo_printf(...) android_OutDebugString(ANDROID_LOG_DEBUG, __VA_ARGS__)
#else
#define jo_printf(...) fprintf(stdout, __VA_ARGS__)
#endif  /*ANDROID*/


struct clog_t
{
	ENUM_LOG_MODE  mode;
	ENUM_LOG_LEVEL level;
	FILE* fp;
	char  fname[CLOG_PATH_LENGTH];
};

struct clog_t global_log;


int is_file_exist(const char* filename);
unsigned long get_file_size(const char *filename);

int logtime_str(char* szbuf, int buflen);
char* loglevel_str(ENUM_LOG_LEVEL level);


int log_init(struct clog_t *plog,const char* fname)
{
	char filename[512];
	if(!plog) return -1;
	if(!fname) return -2;

	plog->level	= LOGL_FATAL;
	plog->mode	= LOG_OUTPUT_STDOUT;
	
	snprintf(filename, 512, "%s/%s", LOG_APP_PATH, fname);

	plog->fp = fopen(fname, "a+");
	if(plog->fp)
		return 0;
	else
		return -2;
}

int log_clean(struct clog_t *plog)
{
	if(plog && plog->fp){
		fclose(plog->fp);
		plog->fp = 0;
	}
	return 0;
}

int log_set_level(struct clog_t *plog, ENUM_LOG_LEVEL level)
{
	if(!plog) return -1;
	plog->level = level;
	return 0;
}
int	log_set_mode(struct clog_t *plog, ENUM_LOG_MODE mode)
{
	if(!plog) return -1;
	plog->mode = mode;
	return 0;
}


void log_trace(struct clog_t *plog, ENUM_LOG_LEVEL level, const char *format,...)
{
	va_list ap;
	char buf[CLOG_BUF_LENGTH]={0};
	char sztime[64] ={0}; 
	
	if(!plog) return;
	if(level < plog->level) return;
  
	va_start(ap, format);
	vsnprintf(buf, CLOG_BUF_LENGTH, format, ap);
	va_end(ap);
	
	logtime_str(sztime, sizeof(sztime));
	switch(plog->mode)
	{
	case LOG_OUTPUT_FILE:
		if(plog->fp)
		{
			fprintf(plog->fp, CLOG_MESSAGE_FORMAT, sztime, loglevel_str(level), buf);
			//fflush(plog->fp);
		}
		break;
	case LOG_OUTPUT_STDOUT_FILE:
		jo_printf(CLOG_MESSAGE_FORMAT, sztime, loglevel_str(level), buf);
		if(plog->fp)
		{
			fprintf(plog->fp, CLOG_MESSAGE_FORMAT, sztime, loglevel_str(level), buf);
			//fflush(plog->fp);
		}
		break;
	case LOG_OUTPUT_STDOUT:
	default:	
		jo_printf(CLOG_MESSAGE_FORMAT, sztime, loglevel_str(level), buf);
		//fprintf(stdout, CLOG_MESSAGE_FORMAT, logtime_str(), loglevel_str(level), buf);
		break;
	}
}

#define CHARS_ON_LINE	16
#define MARGIN_MAX		20
char get_ascii(char ch)
{ 
	if (ch <= 9) return ch + '0';
	if (ch >= 10 && ch <= 15) return ch - 10 + 'A';
	return '.';
}

void log_trace_octect(struct clog_t *plog, ENUM_LOG_LEVEL level, char *data, int count)
{
	int margin = 0;
	char hexbyte[CHARS_ON_LINE * 5];
	int  ch;
	char marginChar [MARGIN_MAX + 1];
	char outbuf[CLOG_BUF_LENGTH];
	int lines, i, j, hexInd, ascInd, iter;

	if(!plog)			return;
	if(!data || count <= 0) return;


	lines = count / CHARS_ON_LINE;
//	if(lines>5) lines=5; //++limit 5 lines

	if (count % CHARS_ON_LINE) lines++;
	memset (marginChar, ' ', MARGIN_MAX + 1);
	if (margin < 0) margin = 0;
	if (margin > MARGIN_MAX) margin = MARGIN_MAX;
	marginChar[margin] = '\0';

	for (i = 0; i < lines; i++)
	{
		hexInd = 0;
		ascInd = 4 + CHARS_ON_LINE * 3;
		memset(hexbyte, ' ', CHARS_ON_LINE * 5);

		for (j = 0; j < CHARS_ON_LINE; j++) 
		{
			if ( (iter = j + i * CHARS_ON_LINE) >= count)
				break;

			ch = ((char *)data)[iter];
			hexbyte[hexInd++] = get_ascii((char)((ch>>4)&15));
			hexbyte[hexInd++] = get_ascii((char)(ch&15));
			hexInd++;
			hexbyte[ascInd++] = (ch>=(char)32 && ch<=(char)126) ? ch : '.';
		}
		hexbyte[CHARS_ON_LINE * 4 + 4] = '\0';

		snprintf(outbuf, CLOG_BUF_LENGTH, "%s0x%04X   %s", marginChar, i, hexbyte);
		log_trace(plog, level, "%s\n", outbuf);
	}
	log_trace(plog, level, "\n");

}


char* loglevel_str(ENUM_LOG_LEVEL level)
{
	switch(level)
	{
		case LOGL_TRACE :
			return (char*)"[Trace]";
		case LOGL_DEBUG:
			return (char*)"{Debug}";
		case LOGL_INFO :
			return (char*)"{Info}";
		case LOGL_NOTICE :
			return (char*)"{Notice}";
		case LOGL_WARN:
			return (char*)"{Warning}";
		case LOGL_ERROR	:
			return (char*)"<Error>";
		case LOGL_FATAL:
			return (char*)"<Fatal>";
		case LOGL_DEFAULT :
		default:
			return (char*)"[Def]";
	}
}

int logtime_str(char* szbuf, int buflen)
{
	struct timeval timestamp;
	struct tm tm;
	time_t t;

	if(!szbuf) return -1;
	if(buflen<28) return -2;

	gettimeofday(&timestamp, NULL);
	t = timestamp.tv_sec;
	localtime_r(&t, &tm);
	snprintf(szbuf, buflen,
		"%04d-%02d-%02d %02d:%02d:%02d.%06ld",
		tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
		tm.tm_hour, tm.tm_min, tm.tm_sec, timestamp.tv_usec);

	return 0;
}

int is_file_exist(const char* filename)
{
	int nRet = (0 == access(filename,0)) ? 0 : -1;
	return nRet;
}

unsigned long get_file_size(const char *filename)  
{  
    unsigned long filesize = -1;      
    struct stat statbuff;  

    if(stat(filename, &statbuff) < 0){  
        return filesize;  
    }else{  
        filesize = statbuff.st_size;  
    }  
    return filesize;  
}  


int write_buffer_file(const char *filename, char *buffer, int size)
{
	int    ret = 0;
	int  nwrite = 0;
	FILE* pfile = fopen(filename, "wb");
	if(NULL==pfile) return -4;

	nwrite = fwrite(buffer, 1, size, pfile);
	if(nwrite<size) ret = -5;

	fclose(pfile);
	return ret;
}
